1 using System;
2 using System.Collections.Generic;
3 using UnityEngine;
4 using UnityEngine.Assertions;
5 using Random = UnityEngine.Random;
6
7 namespace ProceduralToolkit.Examples
8 {
9 /// <summary>
10 /// A simple Perlin noise based low poly terrain generator
11 /// </summary>
12 public static class LowPolyTerrainGenerator
13 {
14 [Serializable]
15 public class Config
16 {
17 public Vector3 terrainSize = new Vector3(20, 1, 20);
18 public float cellSize = 1;
19 public float noiseScale = 5;
20 public Gradient gradient;
21 }
22
23 public static MeshDraft TerrainDraft(Config config)
24 {
25 Assert.IsTrue(config.terrainSize.x > 0);
26 Assert.IsTrue(config.terrainSize.z > 0);
27 Assert.IsTrue(config.cellSize > 0);
28
29 var noiseOffset = new Vector2(Random.Range(0f, 100f), Random.Range(0f, 100f));
30
31 int xSegments = Mathf.FloorToInt(config.terrainSize.x/config.cellSize);
32 int zSegments = Mathf.FloorToInt(config.terrainSize.z/config.cellSize);
33
34 float xStep = config.terrainSize.x/xSegments;
35 float zStep = config.terrainSize.z/zSegments;
36 int vertexCount = 6*xSegments*zSegments;
37 var draft = new MeshDraft
38 {
39 name = "Terrain",
40 vertices = new List<Vector3>(vertexCount),
41 triangles = new List<int>(vertexCount),
42 normals = new List<Vector3>(vertexCount),
43 colors = new List<Color>(vertexCount)
44 };
45
46 for (int i = 0; i < vertexCount; i++)
47 {
48 draft.vertices.Add(Vector3.zero);
49 draft.triangles.Add(0);
50 draft.normals.Add(Vector3.zero);
51 draft.colors.Add(Color.black);
52 }
53
54 for (int x = 0; x < xSegments; x++)
55 {
56 for (int z = 0; z < zSegments; z++)
57 {
58 int index0 = 6*(x + z*xSegments);
59 int index1 = index0 + 1;
60 int index2 = index0 + 2;
61 int index3 = index0 + 3;
62 int index4 = index0 + 4;
63 int index5 = index0 + 5;
64
65 float height00 = GetHeight(x + 0, z + 0, xSegments, zSegments, noiseOffset, config.noiseScale);
66 float height01 = GetHeight(x + 0, z + 1, xSegments, zSegments, noiseOffset, config.noiseScale);
67 float height10 = GetHeight(x + 1, z + 0, xSegments, zSegments, noiseOffset, config.noiseScale);
68 float height11 = GetHeight(x + 1, z + 1, xSegments, zSegments, noiseOffset, config.noiseScale);
69
70 var vertex00 = new Vector3((x + 0)*xStep, height00*config.terrainSize.y, (z + 0)*zStep);
71 var vertex01 = new Vector3((x + 0)*xStep, height01*config.terrainSize.y, (z + 1)*zStep);
72 var vertex10 = new Vector3((x + 1)*xStep, height10*config.terrainSize.y, (z + 0)*zStep);
73 var vertex11 = new Vector3((x + 1)*xStep, height11*config.terrainSize.y, (z + 1)*zStep);
74
75 draft.vertices[index0] = vertex00;
76 draft.vertices[index1] = vertex01;
77 draft.vertices[index2] = vertex11;
78 draft.vertices[index3] = vertex00;
79 draft.vertices[index4] = vertex11;
80 draft.vertices[index5] = vertex10;
81
82 draft.colors[index0] = config.gradient.Evaluate(height00);
83 draft.colors[index1] = config.gradient.Evaluate(height01);
84 draft.colors[index2] = config.gradient.Evaluate(height11);
85 draft.colors[index3] = config.gradient.Evaluate(height00);
86 draft.colors[index4] = config.gradient.Evaluate(height11);
87 draft.colors[index5] = config.gradient.Evaluate(height10);
88
89 Vector3 normal000111 = Vector3.Cross(vertex01 - vertex00, vertex11 - vertex00).normalized;
90 Vector3 normal001011 = Vector3.Cross(vertex11 - vertex00, vertex10 - vertex00).normalized;
91
92 draft.normals[index0] = normal000111;
93 draft.normals[index1] = normal000111;
94 draft.normals[index2] = normal000111;
95 draft.normals[index3] = normal001011;
96 draft.normals[index4] = normal001011;
97 draft.normals[index5] = normal001011;
98
99 draft.triangles[index0] = index0;
100 draft.triangles[index1] = index1;
101 draft.triangles[index2] = index2;
102 draft.triangles[index3] = index3;
103 draft.triangles[index4] = index4;
104 draft.triangles[index5] = index5;
105 }
106 }
107
108 return draft;
109 }
110
111 private static float GetHeight(int x, int z, int xSegments, int zSegments, Vector2 noiseOffset, float noiseScale)
112 {
113 float noiseX = noiseScale*x/xSegments + noiseOffset.x;
114 float noiseZ = noiseScale*z/zSegments + noiseOffset.y;
115 return Mathf.PerlinNoise(noiseX, noiseZ);
116 }
117 }
118 }